package net.sureon.common.exception;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;

import net.sureon.common.I18nSupport;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ExceptionUtil {
	private static final Log logger = LogFactory.getLog(ExceptionUtil.class);
	private static final String LOGGER_APP_PREFIX = "SUREON (internal): ";
	private static final String LOGGER_BLOCK_BEGIN = "LOG BLOCK BEGIN";
	private static final String LOGGER_BLOCK_END = "LOG BLOCK END";

	/**
	 * Translate the expection key to the exception string
	 * 
	 * @param theKey
	 *            Execption key
	 * @return the locale specific error message.
	 */
	public static String translateExceptionKey(String theKey) {
		return translateExceptionKey(theKey, null, null);
	}
	
	/**
	 * Translate the expection key to the exception string
	 * 
	 * @param theKey
	 * @param args
	 *            Execption key
	 * @return the locale specific error message.
	 */
	public static String translateExceptionKey(String theKey, Object[] args) {
		return translateExceptionKey(theKey, args, null);
	}	

	/**
	 * Translate the expection key to the exception string
	 * 
	 * @param theKey
	 *            to the exception message
	 * @param args
	 *            for the message.
	 * @return Locale specific error message
	 */
	public static String translateExceptionKey(String theKey, Object[] args,
			Locale locale) {
		// strip off any concatenated message text.
		String keyOnly = null;
		int iStripHere = theKey.indexOf(' ');
		if (iStripHere == (-1)) {
			keyOnly = theKey;
		} else {
			keyOnly = theKey.substring(0, iStripHere);
		}
		return I18nSupport.getMessage(keyOnly, args, locale);
	}

	/**
	 * Writes a nested exception including all of its children, to the log. This
	 * is similar to what ExceptionHandler does, except that it can be called by
	 * routines that are intercepting and otherwise burying errors. (e.g.
	 * daemons that have no direct user output).
	 * 
	 * Note: the prefixes are different between this version and the code that
	 * is located in ExceptionHandler. You can tell the difference in the log by
	 * looking for 'SUREON (internal):'.
	 * 
	 * @param ex
	 * @param locale
	 */
	public static void logException(Throwable ex, Locale locale) {
		List<String> messages = ExceptionUtil.getMessages(ex, locale);
		logger.error(LOGGER_APP_PREFIX + LOGGER_BLOCK_BEGIN);
		logger.error("Stack Trace", ex);
		Iterator messagesIter = messages.iterator();
		while (messagesIter.hasNext()) {
			logger.error(LOGGER_APP_PREFIX + messagesIter.next());
		}
		logger.error(LOGGER_APP_PREFIX + LOGGER_BLOCK_END);
	}

	/**
	 * Version of logException that defaults to english.
	 * 
	 * @param ex
	 */
	public static void logException(Throwable ex) {
		logException(ex, Locale.ENGLISH);
	}

	/**
	 * Get the list of messages from the execption If the exception is an
	 * instance of sureonException then, Process all the throwables of that
	 * exception If the throwable is an instance of sureonException then,
	 * translate the message and add it to the messages list add all the extra
	 * info as message to the messages list else add the message of the
	 * exception to the messages list else add the message of the exception to
	 * the messages list
	 * 
	 * @param ex
	 *            Top level exception
	 * @param locale
	 *            Locale for message translation
	 * @return the list of locale specific messages
	 */
	public static List<String> getMessages(Throwable ex, Locale locale) {
		// construct a DOM object from the exception messages.
		List<String> messages;
		if (ex instanceof SureonException) {
			messages = getMessages((SureonException) ex, locale);
		} else {
			messages = new LinkedList<String>();
			getMessages((Throwable) ex, messages, locale);
		}
		return messages;
	}

	/**
	 *Get the list of messages from the execption Process all the throwables of
	 * that exception If the throwable is an instance of SureonException then,
	 * translate the message and add it to the messages list add all the extra
	 * info as message to the messages list else add the message of the
	 * exception to the messages list
	 * 
	 * @param exWBE
	 *            top level SureonException
	 * @return the list of message by walking the exception tree
	 */
	public static List<String> getMessages(SureonException exWBE) {
		return getMessages(exWBE, null);
	}

	/**
	 * Converts a stack trace to a string with the specified separators.
	 * 
	 * @param excp
	 *            Exception to walk
	 * @param separators
	 *            message seperator string
	 * @return the messages of the exception tree
	 */
	public static String stackTraceToString(Exception excp, String separators) {
		String theSep = separators;
		if (theSep == null) {
			theSep = "\n";
		}
		StringBuffer sb = new StringBuffer();
		StackTraceElement[] ste = excp.getStackTrace();
		for (int idx = 0; idx < ste.length; idx++) {
			sb.append(ste[idx].toString());
			sb.append(theSep);
		}
		return sb.toString();
	}

	/**
	 * Get the list of messages from the current throwable If the exception is
	 * an instance of sureonException then, translate the message and add it to
	 * the messages list add all the extra info as message to the messages list
	 * else add the message of the exception to the messages list
	 * 
	 * @param throwable
	 *            Throwable
	 * @param messages
	 *            List to added message too
	 * @param locale
	 *            Locale for message translation
	 * @return the list of locale specific messages
	 */
	private static List<String> getMessages(Throwable throwable,
			List<String> messages, Locale locale) {
		SureonException curExcpt;
		String curMessage;
		String[] extraInfo;
		int jdx;
		if (throwable instanceof SureonException) {
			curExcpt = (SureonException) throwable;
			curMessage = translateExceptionKey(curExcpt.getMessage(), curExcpt
					.getArguments().toArray(), locale);
			messages.add(curMessage);
			extraInfo = curExcpt.printExtraInfo();
			for (jdx = 0; jdx < extraInfo.length; jdx++) {
				if (extraInfo[jdx].length() > 0) {
					messages.add(extraInfo[jdx]);
				}
			}
		} else {
			String msg = throwable.getLocalizedMessage();
			if (msg == null) {
				msg = throwable.toString();
			}
			messages.add(msg);
		}
		return messages;
	}

}
